﻿<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Diagram with Rulers</title>
  <meta name="description" content="A diagram with Graduated panels at the edges acting as rulers" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Copyright 1998-2019 by Northwoods Software Corporation. -->

  <script src="../release/go.js"></script>
  <script src="../assets/js/goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
  <script id="code">
    function init() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this

      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // create a Diagram for the DIV HTML element
          {
            "undoManager.isEnabled": true,  // enable undo & redo
            scrollMode: go.Diagram.InfiniteScroll,  // allow the diagram to be scrolled beyond content
            padding: 0,  // scales should be allowed right up against the edges of the viewport
            "grid.visible": true
          });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          $(go.Shape, "RoundedRectangle",
            { strokeWidth: 0, portId: "", fromLinkable: true, toLinkable: true },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8 },
            new go.Binding("text", "key"))
        );

      myDiagram.model = new go.GraphLinksModel(
        [
          { key: "Alpha", color: "lightblue" },
          { key: "Beta", color: "orange" },
          { key: "Gamma", color: "lightgreen" },
          { key: "Delta", color: "pink" }
        ],
        [
          { from: "Alpha", to: "Beta" },
          { from: "Alpha", to: "Gamma" },
          { from: "Beta", to: "Beta" },
          { from: "Gamma", to: "Delta" },
          { from: "Delta", to: "Alpha" }
        ]);

      // Keep references to the scales and indicators to easily update them
      var gradScaleHoriz =
        $(go.Node, "Graduated",
          {
            graduatedTickUnit: 10, pickable: false, layerName: "Foreground",
            isInDocumentBounds: false, isAnimated: false
          },
          $(go.Shape, { geometryString: "M0 0 H400" }),
          $(go.Shape, { geometryString: "M0 0 V3", interval: 1 }),
          $(go.Shape, { geometryString: "M0 0 V15", interval: 5 }),
          $(go.TextBlock,
            {
              font: "10px sans-serif",
              interval: 5,
              alignmentFocus: go.Spot.TopLeft,
              segmentOffset: new go.Point(0, 7)
            }
          )
        );

      var gradScaleVert =
        $(go.Node, "Graduated",
          {
            graduatedTickUnit: 10, pickable: false, layerName: "Foreground",
            isInDocumentBounds: false, isAnimated: false
          },
          $(go.Shape, { geometryString: "M0 0 V400" }),
          $(go.Shape, { geometryString: "M0 0 V3", interval: 1, alignmentFocus: go.Spot.Bottom }),
          $(go.Shape, { geometryString: "M0 0 V15", interval: 5, alignmentFocus: go.Spot.Bottom }),
          $(go.TextBlock,
            {
              font: "10px sans-serif",
              segmentOrientation: go.Link.OrientOpposite,
              interval: 5,
              alignmentFocus: go.Spot.BottomLeft,
              segmentOffset: new go.Point(0, -7)
            }
          )
        );

      // These indicators are globally defined so they can be accessed by the div's mouseevents
      gradIndicatorHoriz =
        $(go.Node,
          {
            pickable: false, layerName: "Foreground", visible: false,
            isInDocumentBounds: false, isAnimated: false,
            locationSpot: go.Spot.Top
          },
          $(go.Shape, { geometryString: "M0 0 V15", strokeWidth: 2, stroke: "red" })
        );

      gradIndicatorVert =
        $(go.Node,
          {
            pickable: false, layerName: "Foreground", visible: false,
            isInDocumentBounds: false, isAnimated: false,
            locationSpot: go.Spot.Left
          },
          $(go.Shape, { geometryString: "M0 0 H15", strokeWidth: 2, stroke: "red" })
        );

      // Add listeners to keep the scales/indicators in sync with the viewport
      myDiagram.addDiagramListener("InitialLayoutCompleted", setupScalesAndIndicators);
      myDiagram.addDiagramListener("ViewportBoundsChanged", updateScales);
      myDiagram.addDiagramListener("ViewportBoundsChanged", updateIndicators);
      // Override mousemove Tools such that doMouseMove will keep indicators in sync
      myDiagram.toolManager.doMouseMove = function() {
        go.ToolManager.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.linkingTool.doMouseMove = function() {
        go.LinkingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.draggingTool.doMouseMove = function() {
        go.DraggingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.dragSelectingTool.doMouseMove = function() {
        go.DragSelectingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      // No need to override PanningTool since the ViewportBoundsChanged listener will fire

      function setupScalesAndIndicators() {
        var vb = myDiagram.viewportBounds;
        myDiagram.startTransaction("add scales");
        updateScales();
        // Add each node to the diagram
        myDiagram.add(gradScaleHoriz);
        myDiagram.add(gradScaleVert);
        myDiagram.add(gradIndicatorHoriz);
        myDiagram.add(gradIndicatorVert);
        myDiagram.commitTransaction("add scales");
      }

      function updateScales() {
        var vb = myDiagram.viewportBounds;
        myDiagram.startTransaction("update scales");
        // Update properties of horizontal scale to reflect viewport
        gradScaleHoriz.location = new go.Point(vb.x, vb.y);
        gradScaleHoriz.graduatedMin = vb.x;
        gradScaleHoriz.graduatedMax = vb.right;
        gradScaleHoriz.scale = 1 / myDiagram.scale;
        // Update properties of vertical scale to reflect viewport
        gradScaleVert.location = new go.Point(vb.x, vb.y);
        gradScaleVert.graduatedMin = vb.y;
        gradScaleVert.graduatedMax = vb.bottom;
        gradScaleVert.scale = 1 / myDiagram.scale;
        myDiagram.commitTransaction("update scales");
      }

      function updateIndicators() {
        var vb = myDiagram.viewportBounds;
        var mouseCoords = myDiagram.lastInput.documentPoint;
        myDiagram.startTransaction("update indicators");
        // Keep the indicators in line with the mouse as viewport changes or mouse moves
        gradIndicatorHoriz.location = new go.Point(Math.max(mouseCoords.x, vb.x), vb.y);
        gradIndicatorHoriz.scale = 1 / myDiagram.scale;
        gradIndicatorVert.location = new go.Point(vb.x, Math.max(mouseCoords.y, vb.y));
        gradIndicatorVert.scale = 1 / myDiagram.scale;
        myDiagram.commitTransaction("update indicators");
      }
    }

    // Show indicators on mouseover of the diagram div
    function showIndicators() {
      myDiagram.startTransaction("show indicators");
      gradIndicatorHoriz.visible = true;
      gradIndicatorVert.visible = true;
      myDiagram.commitTransaction("show indicators");
    }

    // Hide indicators on mouseout of the diagram div
    function hideIndicators() {
      myDiagram.startTransaction("hide indicators");
      gradIndicatorHoriz.visible = false;
      gradIndicatorVert.visible = false;
      myDiagram.commitTransaction("hide indicators");
    }

  </script>
</head>
<body onload="init()">
<div id="sample">
  <!-- The DIV for the Diagram needs an explicit size or else we won't see anything.
       This also adds a border to help see the edges of the viewport. -->
  <div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"
       onmouseover="showIndicators()" onmouseout="hideIndicators()"></div>
  <p>
    This sample demonstrates a diagram with rulers at its edges and indicators which display the mouse's position.
  </p>
  <p>
    The rulers are implemented using <a href="../intro/graduatedPanels.html">Graduated Panels</a>. The main element of each panel is sized
    according to the width/height of the viewport, with the <a>Panel.graduatedMin</a> and <a>Panel.graduatedMax</a>
    being set to the edges of the viewport.
  </p>
  <p>
    Event listeners and Tool overrides are used to keep the rulers and indicators in sync as the viewport bounds change
    or the mouse moves around the diagram.
    <ul>
      <li>
        <code>ViewportBoundsChanged</code> listeners are used to keep the rulers and indicators against
        the edge of the diagram and to update the min and max values of the rulers.
      </li>
      <li>
        An <code>InitialLayoutCompleted</code> listener is used for initial placement after the diagram
        has positioned the rest of the nodes.
      </li>
      <li>
        <a>ToolManager.doMouseMove</a>, <a>LinkingTool.doMouseMove</a>, <a>DraggingTool.doMouseMove</a>,
        and <a>DragSelectingTool.doMouseMove</a> are overridden to update the mouse indicators after executing
        their default behavior. Each is overridden so that whichever tool is active will properly adjust the
        indicators in addition to its normal functionality.
      </li>
      <li>
        Finally, the Diagram's div uses <code>onmouseover</code> and <code>onmouseout</code> events to show or hide the
        indicators as the mouse moves into or out of the diagram.
      </li>
    </ul>
  </p>
  <p>
    The rulers depend on the <a>Diagram.viewportBounds</a>, which can be impacted by changes to the <a>Diagram.documentBounds</a>.
    Therefore, the <a>Part.isInDocumentBounds</a> property is set to <code>false</code> to prevent circular
    dependencies in cases where the rulers grow/shrink to fit the viewport.
  </p>
</div>
</body>
</html>